package com.zk.bytecode.trace;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;


/**
 * 到达当前方法节点的Trace和当前方法
 * 其中，当前节点方法记录的是当前节点的方法信息，使用StackTraceElement，但仅有三个属性是有效的，文件，类名，方法名
 * Holder包括 当前方法，方法到达路径 stackTrace
 * 方法被调用节点清单（StackTraceElement），即节点相关后继调用信息
 */
public class ElementTree {
    // 上级节点
    ElementTree parent = null;
    // 到达当前节点的StackTrace列表,当前节点的方法标识
    StackTraceMethod current;
    List<ElementNode> nodes = new ArrayList<>();

    public ElementTree() {
    }

    /**
     * 创建根节点
     *
     * @param stackTraceElements
     * @return
     */
    public static ElementTree createRoot(List<StackTraceElement> stackTraceElements) {
        ElementTree holder = new ElementTree();
        holder.current = new StackTraceMethod(stackTraceElements);
        return holder;
    }

    /**
     * 创建根节点
     *
     * @param stackTraceElements
     * @return
     */
    public static ElementTree createChild(ElementTree parent, List<StackTraceElement> stackTraceElements) {
        ElementTree holder = new ElementTree();
        holder.current = new StackTraceMethod(stackTraceElements);
        holder.parent = parent;
        return holder;
    }


    /**
     * 创建当前节点的直接子节点 TODO 怀疑是否有存在意义
     *
     * @param stackTraceElements
     * @return
     */
    ElementTree createChild(List<StackTraceElement> stackTraceElements) {
        ElementTree child = new ElementTree();
        child.parent = this;
        child.current = new StackTraceMethod(stackTraceElements);

        return child;
    }

    public ElementNode currentNode(StackTraceElement currentStackTrace) {
        ElementNode currentNode = null;
        // 如果子节点不为空，遍历子节点，找到新的根节点或待合并节点。
        for (ElementNode node1 : this.nodes) {
            if (Objects.equals(node1.invokeElement, currentStackTrace)) {
                currentNode = node1;
                break;
            }
        }
        if (currentNode == null) {
            currentNode = new ElementNode(this, currentStackTrace);
            this.nodes.add(currentNode);
        }
        return currentNode;
    }

    /**
     * 合并给定节点到Root节点或下级节点
     * 当前节点为root时，所有trace数组都应该是当前节点的相同或子节点，这是由实际场景确定的。
     *
     * @param src 传入的源List
     * @return 当合并成功后，返回true,否则返回false
     */
    public boolean mergeStack(final List<StackTraceElement> src) {
        final List<StackTraceElement> srcStackTrace = Collections.unmodifiableList(src);
        final int srcDepth = srcStackTrace.size();
        final int currentDepth = current.depth(); // 入口方法的Stack深度
        final int equalSize = this.current.equalSize(srcStackTrace);// stackElements 与 base 相同元素的数量
        // 当前current.stack()与srcStackTrace无重合时，不可以合并。
        if (equalSize <= 0) {
            return false;
        }

        // srcStackTrace == current.stack() 元素完全相同时，直接返回，一般是根节点直接返回或循环调用场景
        if (currentDepth == equalSize && srcDepth == equalSize) {
            return true;
        }

        //  合并节点场景 传入节点和当前节点有交叉部分，需要合并后，创建双子节点
        if (currentDepth > equalSize && srcDepth > equalSize) {
            List<StackTraceElement> equalStack = srcStackTrace.subList(srcStackTrace.size() - equalSize, srcStackTrace.size());
            // 先执行节点缩进翻转
            this.mergeStack(equalStack);
            // 基于翻转后的当前节点，执行节点追加
            return this.mergeStack(srcStackTrace);
        }


        // 传入节点是当前节点的上级节点，需要将当前节点转化为传入节点的下级节点
        if (currentDepth > equalSize && srcDepth == equalSize) {
            StackTraceMethod newMethod = new StackTraceMethod(srcStackTrace);

            // 复制thisHolder
            ElementTree thisHolder = new ElementTree();
            thisHolder.current = this.current;
            thisHolder.nodes = this.nodes;
            thisHolder.parent = this;

            // 调整thisHolder的stack结构，让其基于新的parent节点
            List<StackTraceElement> thisHolderStack = thisHolder.current.stack();
            thisHolderStack = thisHolderStack.subList(0, thisHolderStack.size() - srcStackTrace.size());
            thisHolder.current.stack(thisHolderStack);

            this.current = newMethod;
            this.nodes = new ArrayList<>();

            ElementNode node = new ElementNode(this, srcStackTrace.get(0));
            this.nodes.add(node);
            node.childs.add(thisHolder);
            return true;
        }


        // 传入节点是当前节点的子节点，需要做子节点增加
        if (currentDepth == equalSize && srcDepth > equalSize) {
            // 确定待增加的子节点Node，即当前方法的lineNumber， 先要确定当前StackTraceElement，即与当前方法同级别的StackTraceElement
            // StackTrace是倒序的，需要越小，越靠近栈顶
            StackTraceElement currentStackTraceElement = srcStackTrace.get(srcDepth - currentDepth);
            List<StackTraceElement> childStack = srcStackTrace.subList(0, srcDepth - currentDepth);

            // 获取当前trace的ChildNode，如果没有，则添加Node
            ElementNode currentNode = currentNode(currentStackTraceElement);
            currentNode.addToChild(childStack);
            return true;
        }

        // equalSize 不可能大于任意一个，所以不存在其他情况
        //应该不会发生，全部逻辑已经完备覆盖
        return false;
    }


    /**
     * 特定方法被调用的节点
     */
    public static class ElementNode {
        private ElementTree thisHolder;
        private StackTraceElement invokeElement;
        private List<ElementTree> childs = new ArrayList<>();

        public StackTraceElement getInvokeElement() {
            return invokeElement;
        }

        public ElementTree getThisHolder() {
            return thisHolder;
        }

        public List<ElementTree> getChilds() {
            return childs;
        }

        public ElementNode(ElementTree thisHolder) {
            this.thisHolder = thisHolder;

        }

        public ElementNode(ElementTree thisHolder, StackTraceElement stackTraceElement) {
            this.thisHolder = thisHolder;
            this.invokeElement = stackTraceElement;
        }


        public boolean addToChild(List<StackTraceElement> subStackTraceList) {
            List<ElementTree> childs = this.childs;

            for (ElementTree childHolder : childs) {
                if (childHolder.mergeStack(subStackTraceList)) { // 执行合并
                    return true;
                }
            }

            // 当没有子节点时或匹配节点，直接添加目标StackTrace作为子节点。
            ElementTree newChildHolder = ElementTree.createChild(thisHolder, subStackTraceList);
            childs.add(newChildHolder);
            return true;
        }
    }
}